#pragma once
#include <iostream>
#include <exception>
#include <mutex>

#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/mman.h>
#endif

using std::cout;
using std::endl;

// 定长内存池
// template<size_t N>
// class ObjectPool
//{
// private:
// };

inline static void *Alloc(size_t kpage)
{
#ifdef _WIN32
	void *ptr = VirtualAlloc(0, kpage * (1 << 12), MEM_COMMIT | MEM_RESERVE,
							 PAGE_READWRITE); // 1 << 12 : 4096-->4KB
	if (ptr == nullptr)
		throw std::bad_alloc();
#else
	// linux下brk mmap等
	void *ptr = ::mmap(nullptr, kpage * (1 << 12),
					   PROT_READ | PROT_WRITE,
					   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); //::表示强制要求调用全局作用域中的mmap(即libc中的mmap),防止误调用
	if (ptr == MAP_FAILED)
		throw std::bad_alloc();
#endif
	return ptr;
}

template <class T>
class ObjectPool
{
public:
	ObjectPool()
	{
#if defined(__x86_64__) | defined(_WIN64)
		size_t bytes = 4 * 1024 * 1024; // 4MB
#else
		size_t bytes = 128 * 1024; // 128KB
#endif
		_memory = (char *)Alloc(bytes >> 12);
		_remainBytes = bytes;
		if (_memory == nullptr)
		{
			throw std::bad_alloc();
		}
	}

	
	T *New()
	{
		_mutex.lock();
		T *obj = nullptr;
		// 优先使用还回来的内存块,再次重复利用:由于都是T类型,因此内存大小符合
		if (_freelist)
		{
			obj = (T *)_freelist;
			_freelist = *(void **)_freelist;
		}
		else
		{
			size_t obj_size = sizeof(T) < sizeof(void *) ? sizeof(void *) : sizeof(T); // 对象大小总是能够存下相应平台下的指针
			// 剩余内存不够一个对象大小时,重新开大块空间:暂不考虑剩余的内存碎片问题
			if (_remainBytes < obj_size)
			{
#if defined(__x86_64__) | defined(_WIN64)
				size_t bytes = 4 * 1024 * 1024; // 4
#else
				size_t bytes = 128 * 1024; // 128KB
#endif
				_memory = (char *)Alloc(bytes >> 12);
				_remainBytes = bytes;
				if (_memory == nullptr)
				{
					throw std::bad_alloc();
				}
			}
			obj = (T *)_memory;
			_memory += obj_size;
			_remainBytes -= obj_size;
		}

		// 定位new初始化
		new (obj) T;
		_mutex.unlock();
		return obj;
	}
	void Delete(T *obj)
	{
		// if (_freelist == nullptr)
		//{
		//	_freelist = obj;//obj执行要释放的空间
		//	//*(int*)obj = nullptr;//直接取出首部4个字节:本身就是要释放空间,直接覆盖即可-无法解决平台问题:32or64
		//	*(void**)obj = nullptr;//非常巧妙的平台自适应指针大小写法:本身解引用后就是拿到一个指针
		// }
		if (obj == nullptr)
			return;
		_mutex.lock();
		obj->~T(); // 显示调用析构,释放相应资源:对于内置类型会自动豁免
		// 头插即可,否则需要找链表尾部:为空时逻辑亦可复用
		*(void **)obj = _freelist;
		_freelist = obj;
		_mutex.unlock();
	}

private:
	char *_memory = nullptr;   // 指向大块内存的指针
	size_t _remainBytes = 0;   // 大块内存剩余的字节
	void *_freelist = nullptr; // 归还内存中的自由链表头指针
	std::mutex _mutex;		   // 多线程保护锁
};